package presentation.controller;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import logic.facade.GroupFacade;
import logic.facade.SubjectFacade;
import logic.facade.TeacherFacade;
import logic.simplex.model.*;
import logic.simplex.solver.InfinitySolutionException;
import logic.simplex.solver.LPSolver;
import logic.simplex.solver.LPSolverFactory;
import presentation.view.AuxiliarFrame;
import presentation.view.PanelCreateSchedule;
import presentation.view.PanelSearchSubjects;
import vo.*;

/**
 *
 * @author Alexander
 */
public class SchedulePanelControl {

    public static final int CREATE_SCHEDULE = 1;
    public static final int UPDATE_SCHEDULE = 2;

    public void armarHorario(Integer[] codigos, final int option, final AuxiliarFrame parent, final PanelSearchSubjects previous) {
        final List<SubjectVO> subjects = new ArrayList<>();
        final List<SubjectVO> newSubjects = new ArrayList<>();
        for (int i = 0; i < codigos.length; i++) {
            subjects.add((SubjectVO) new SubjectFacade().find(codigos[i]));
        }
        final JPanel panelSelectGroups = new JPanel();
        GroupLayout layout = new GroupLayout(panelSelectGroups);
        panelSelectGroups.setLayout(layout);
        panelSelectGroups.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Selección de Grupos", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI Light", 1, 14), new java.awt.Color(204, 0, 204))); // NOI18N
        GroupLayout.SequentialGroup leftToRight = layout.createSequentialGroup();
        GroupLayout.ParallelGroup column = layout.createParallelGroup(GroupLayout.Alignment.CENTER);
        GroupLayout.SequentialGroup topToBottom = layout.createSequentialGroup();

        final ArrayList<ButtonGroup> buttonGroups = new ArrayList<>();
        for (SubjectVO subject : subjects) {
            JPanel panel = new JPanel();
            panel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, subject.getName(), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI Light", 0, 14), Color.BLUE)); // NOI18N

            GroupLayout panelLayout = new GroupLayout(panel);
            GroupLayout.SequentialGroup leftRight = panelLayout.createSequentialGroup();
            GroupLayout.SequentialGroup topBottom = panelLayout.createSequentialGroup();
            GroupLayout.ParallelGroup firstColumn = panelLayout.createParallelGroup();
            GroupLayout.ParallelGroup secondColumn = panelLayout.createParallelGroup();
            GroupLayout.ParallelGroup thirdColumn = panelLayout.createParallelGroup();
            GroupLayout.ParallelGroup fourthColumn = panelLayout.createParallelGroup();
            
            panel.setLayout(panelLayout);
            
            Font font = new Font("Segoe UI Light", 1, 14);
            GroupLayout.ParallelGroup columns = panelLayout.createParallelGroup();
            JLabel column1 = new JLabel("Grupo");
            column1.setFont(font);
            firstColumn.addComponent(column1);
            columns.addComponent(column1);
            JLabel column2 = new JLabel("Horario");
            column2.setFont(font);
            secondColumn.addComponent(column2);
            columns.addComponent(column2);
            JLabel column3 = new JLabel("Profesor");
            column3.setFont(font);
            thirdColumn.addComponent(column3);
            columns.addComponent(column3);
            JLabel column4 = new JLabel("Reputación");
            column4.setFont(font);
            fourthColumn.addComponent(column4);
            columns.addComponent(column4);
            topBottom.addGroup(columns);

            topBottom.addGap(20);

            ButtonGroup buttonGroup = new ButtonGroup();
            for (GroupVO grupo : subject.getGroups()) {
                JRadioButton button = new JRadioButton("Grupo " + grupo.getGroupNumber());
                button.setSelected(true);
                buttonGroup.add(button);
                firstColumn.addComponent(button);
                topBottom.addComponent(button);
                for (ClassDayVO dia : grupo.getClassDays()) {
                    GroupLayout.ParallelGroup row = panelLayout.createParallelGroup();
                    String endTime = new Integer(dia.getHour() + dia.getSpan()).toString();
                    JLabel labelTime = new JLabel(dia.getDay() + " " + dia.getHour() + " - " + endTime);
                    secondColumn.addComponent(labelTime);
                    row.addComponent(labelTime);
                    TeacherVO teacher = (TeacherVO) new TeacherFacade().find(dia.getTeacherId());
                    JLabel labelTeacher = new JLabel(teacher.toString());
                    thirdColumn.addComponent(labelTeacher);
                    row.addComponent(labelTeacher);
                    JLabel labelRep = new JLabel(new Double(teacher.getReputation()).toString());
                    fourthColumn.addComponent(labelRep, GroupLayout.Alignment.CENTER);
                    row.addComponent(labelRep, GroupLayout.Alignment.CENTER);
                    topBottom.addGroup(row);
                }
            }
            buttonGroups.add(buttonGroup);

            leftRight.addGroup(firstColumn);
            leftRight.addGap(30);
            leftRight.addGroup(secondColumn);
            leftRight.addGap(30);
            leftRight.addGroup(thirdColumn);
            leftRight.addGap(30);
            leftRight.addGroup(fourthColumn);

            panelLayout.setHorizontalGroup(leftRight);
            panelLayout.setVerticalGroup(topBottom);

            topToBottom.addComponent(panel);
            column.addComponent(panel);
        }

        JButton buttonBack = new JButton("Volver");
        buttonBack.addActionListener(new java.awt.event.ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                parent.cambiarPanel(previous);
            }
        });
        JButton buttonCreateSchedule = new JButton("Crear Horario");
        buttonCreateSchedule.addActionListener(new java.awt.event.ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                for (SubjectVO sub : subjects) {
                    SubjectVO sub2 = new SubjectVO();
                    StudentVO active = new ControlPanelMyAccount().findByIp();
                    sub2.setCode((sub.getCode() * 100) + active.getCalendarId());
                    sub2.setCalendarId(active.getCalendarId());
                    sub2.setName(sub.getName());
                    sub2.setCredits(sub.getCredits());
                    sub2.setGroups(new ArrayList<GroupVO>());
                    newSubjects.add(sub2);
                }
                ArrayList<GroupVO> groups = new ArrayList<>();
                int j = 0;
                for (ButtonGroup buttonG : buttonGroups) {
                    Enumeration<AbstractButton> buttons = buttonG.getElements();
                    int i = 0;
                    while (buttons.hasMoreElements()) {
                        JRadioButton button = (JRadioButton) buttons.nextElement();
                        if (button.isSelected()) {
                            GroupVO group = subjects.get(j).getGroups().get(i);
                            group.setId(newSubjects.get(j).getCode() + "-" + group.getGroupNumber());
                            group.setSubjectId(newSubjects.get(j).getCode());
                            for (ClassDayVO day : group.getClassDays()) {
                                day.setGroupId(group.getId());
//                                day.setId(null);
                            }
                            groups.add(group);
                        }
                        i++;
                    }
                    j++;
                }
                int k = 0;
                for (SubjectVO sub : newSubjects) {
                    sub.getGroups().add(groups.get(k));
                    k++;
                }

                parent.cambiarPanel(new PanelCreateSchedule(parent, panelSelectGroups, newSubjects, option));
                newSubjects.removeAll(newSubjects);
            }
        });

        column.addComponent(buttonCreateSchedule);
        
        topToBottom.addGap(50);
        topToBottom.addGroup(layout.createParallelGroup().addComponent(buttonCreateSchedule).addComponent(buttonBack));

        leftToRight.addGroup(column);
        leftToRight.addComponent(buttonBack);

        layout.setHorizontalGroup(leftToRight);
        layout.setVerticalGroup(topToBottom);

        parent.cambiarPanel(panelSelectGroups);
    }

    public ArrayList<SubjectVO> generateSchedule(Integer[] codigos, final int option, final AuxiliarFrame parent, final PanelSearchSubjects previous) throws InfinitySolutionException {

        //Posible relocación de clases
        final List<SubjectVO> subjects = new ArrayList<>();
        //Encontrar las materias...
        for (int i = 0; i < codigos.length; i++) {
            subjects.add((SubjectVO) new SubjectFacade().find(codigos[i]));
        }



        LPProblem schedule = new LPProblem();
        LPFunction function = new LPFunction();
        //Tabla para hallar los cosos esos
        ArrayList<LPVariable> timeTable[][] = new ArrayList[8][24];

        ArrayList<LPVariable> variables = new ArrayList();

        //Para cada materia
        for (SubjectVO subject : subjects) {
            //Para cada grupo

            ArrayList<LPVariable> variablesPerGroup = new ArrayList();
            for (GroupVO group : subject.getGroups()) {
                //Añadir a la lista de grupos crear variable
                LPVariable groupAsVariable = new LPVariable(group.getId()); //Añadir a función objetivo (buscar profe y tales) (Y si los profesores no tienen calificacion o es 0)
                variables.add(groupAsVariable);
                variablesPerGroup.add(groupAsVariable);
                TeacherVO teacher = (TeacherVO) new TeacherFacade().find(group.getClassDays().get(0).getTeacherId());
                function.plus(teacher.getReputation(), groupAsVariable);

                for (ClassDayVO dia : group.getClassDays()) {
                    //Añadir a la correspondencia de la tabla temporal
                    int amountOfHours = (int) dia.getSpan();
                    int day = getNumericDay(dia.getDay());
                    int hour = (int) dia.getHour();

                    for (int i = amountOfHours; i > 0; i--) {
                        if (timeTable[day][hour] == null) {
                            timeTable[day][hour] = new ArrayList<>();
                        }
                        timeTable[day][hour].add(groupAsVariable);
                        hour++;

                    }

                }
            }
            //Crear restricciones de grupo
            LPConstraint groupConstraint = new LPConstraint();
            for (LPVariable eachGroup : variablesPerGroup) {
                groupConstraint.plus(1, eachGroup);
            }
            groupConstraint.lessOrEqualTo(1);
            schedule.addConstraint(groupConstraint);
        }
        schedule.setObjectiveFunction(function);
        //Crear restricciones de horario

        for (int i = 1; i <= 7; i++) {
            for (int j = 0; j <= 23; j++) {
                if (timeTable[i][j] != null) {
                    if (!timeTable[i][j].isEmpty()) {
                        LPConstraint hourConstraint = new LPConstraint();
                        for (LPVariable eachGroup : timeTable[i][j]) {
                            hourConstraint.plus(1, eachGroup);
                        }
                        hourConstraint.lessOrEqualTo(1);
                        schedule.addConstraint(hourConstraint);
                    }
                }


            }

        }
        //Solucionar

        LPSolver solver = LPSolverFactory.newSolver();
        LPSolution solutionSchedule = solver.solve(schedule);

        ArrayList<LPVariable> solutions = new ArrayList();
        for (LPVariable eachVariable : variables) {
            if (solutionSchedule.getSolution(eachVariable) == 1) {
                solutions.add(eachVariable);
            }
        }

        //hacer mapeo de variables aceptables a grupos aceptados
        ArrayList<GroupVO> groups = new ArrayList<>();
        for (LPVariable var : solutions) {
            groups.add((GroupVO) new GroupFacade().find(var.getName()));
        }

        ArrayList<SubjectVO> newSubjects = new ArrayList<>();
        for (GroupVO group : groups) {
            SubjectVO sub = (SubjectVO) new SubjectFacade().find(group.getSubjectId());
            SubjectVO sub2 = new SubjectVO();
            StudentVO active = new ControlPanelMyAccount().findByIp();
            sub2.setCode((sub.getCode() * 100) + active.getCalendarId());
            sub2.setCalendarId(active.getCalendarId());
            sub2.setName(sub.getName());
            sub2.setCredits(sub.getCredits());
            sub2.setGroups(new ArrayList<GroupVO>());
            sub2.getGroups().add(group);
            group.setId(sub2.getCode() + "-" + group.getGroupNumber());
            group.setSubjectId(sub2.getCode());
            for (ClassDayVO day : group.getClassDays()) {
                day.setGroupId(group.getId());
            }
            newSubjects.add(sub2);
        }

        //Construir horario grafico a partirde el
        return newSubjects;

    }

    public int getNumericDay(String dayString) {
        //TODO {"Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"}
        //Posible salirse de boundary de la matriz?
        int day;
        switch (dayString) {
            case "Lunes":
                day = 1;
                break;
            case "Martes":
                day = 2;
                break;
            case "Miercoles":
                day = 3;
                break;
            case "Jueves":
                day = 4;
                break;
            case "Viernes":
                day = 5;
                break;

            case "Sabado":
                day = 6;
                break;
            case "Domingo":
                day = 7;
                break;

            default:
                day = 0;
                break;
        }
        return day;
    }

    public void generateScheduleReport(PanelCreateSchedule parent, JTable graphicSchedule) throws DocumentException, FileNotFoundException {
        //Stream
        allocateFileDestiny();

        if (this.routeToFile != null) {

            Document document = new Document(PageSize.LETTER_LANDSCAPE);
            PdfWriter.getInstance(document, new FileOutputStream(this.routeToFile + ".pdf"));
            document.open();

            float[] colsWidth = {1f, 2f, 2f, 2f, 2f, 2f, 2f, 2f}; // Code 1
            PdfPTable schedule = new PdfPTable(colsWidth);

            schedule.setWidthPercentage(100); // Code 2
            schedule.setHorizontalAlignment(Element.ALIGN_CENTER);//Code 3

            schedule.addCell("Hora");
            schedule.addCell("Domingo");
            schedule.addCell("Lunes");
            schedule.addCell("Martes");
            schedule.addCell("Miercoles");
            schedule.addCell("Jueves");
            schedule.addCell("Viernes");
            schedule.addCell("Sabado");


            for (int i = 0; i < 15; i++) {
                for (int j = 0; j < 8; j++) {
                    if (graphicSchedule.getModel().getValueAt(i, j) != null) {
                        schedule.addCell(graphicSchedule.getModel().getValueAt(i, j).toString());
                    } else {
                        schedule.addCell("");
                    }
                }
            }


            document.add(schedule);
            document.close();
            try {
                Desktop.getDesktop().open(new File(routeToFile.getPath() + ".pdf"));
            } catch (IOException ex) {
                Logger.getLogger(SchedulePanelControl.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

    }
    private File routeToFile = null;

    public void allocateFileDestiny() {
        FileNameExtensionFilter filter = new FileNameExtensionFilter("Archivo PDF", "pdf", "PDF");
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileFilter(filter);
        int result = fileChooser.showSaveDialog(null);
        if (result == JFileChooser.APPROVE_OPTION) {
            this.routeToFile = fileChooser.getSelectedFile().getAbsoluteFile();
        }
    }
}
